﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Declarations;
using Declarations.Media;
using Declarations.Players;
using iNet.Media.Imaging;
using iNet.Tasks;

namespace iNet.Media.Video
{
	/// <summary>
	/// 內建影片解碼工作的基底類別。
	/// </summary>
	public abstract class BaseVideoDecodeTask : VideoDecodeTask
	{
		#region 靜態欄位
		static readonly IMediaPlayerFactory _MediaPlayerFactory = new Implementation.MediaPlayerFactory(new string[] {
			"-I", 
			"dumy",  
			"--no-audio",
			"--ignore-config", 
			"--no-osd",
			"--disable-screensaver",
		});
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 BaseVideoDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">解碼的影音容器格式。</param>
		public BaseVideoDecodeTask(IMediaFormat decodingFormat)
			: base(decodingFormat)
		{ }
		/// <summary>
		/// 初始化新的 BaseVideoDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">解碼的影音容器格式。</param>
		/// <param name="source">影片的儲存來源。</param>
		public BaseVideoDecodeTask(IMediaFormat decodingFormat, MediaStorage source)
			: base(decodingFormat, source)
		{ }
		#endregion
		#region Decode
		/// <summary>
		/// 解碼影片。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="source">影片儲存來源。</param>
		/// <param name="mediaDecodeReqs">媒體解碼請求。</param>
		/// <param name="videoDecodeReqs">影片解碼請求。</param>
		/// <returns>True 表示解碼成功，False 表示失敗。</returns>
		protected override bool Decode(TaskVersion version, MediaStorage source, MediaDecodeRequests mediaDecodeReqs, VideoDecodeRequests videoDecodeReqs)
		{
			//使用 VLC 解碼影片資訊
			bool decodeDuration = ((videoDecodeReqs & Video.VideoDecodeRequests.DecodeDuration) != 0);
			bool decodeVideoPixelSize = ((videoDecodeReqs & Video.VideoDecodeRequests.DecodeVideoPixelSize) != 0);
			bool decodeThumb = ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeThumbnailImage) != 0);
			if ((videoDecodeReqs & (VideoDecodeRequests.DecodeDuration | Video.VideoDecodeRequests.DecodeVideoPixelSize)) != 0 || decodeThumb)
			{
				//取得檔案路徑
				string videoPath;
				bool lockedAsFile = false;
				switch (source.StorageType)
				{
					case MediaStorageType.Uri:
						videoPath = source.Uri.AbsoluteUri;
						break;
					case MediaStorageType.WebRequest:
						videoPath = source.WebRequest.RequestUri.AbsoluteUri;
						break;
					default:
						if (!this.GetMediaStorageFile(version, source, 50, out videoPath))
							throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);
						lockedAsFile = true;
						break;
				}

				//解碼影片資訊
				Declarations.Media.IMedia media = null;
				IVideoPlayer videoPlayer = null;
				try
				{
					//開啟媒體
					if (lockedAsFile)
						media = _MediaPlayerFactory.CreateMedia<IMediaFromFile>(videoPath);
					else
						media = _MediaPlayerFactory.CreateMedia<Declarations.Media.IMedia>(videoPath);
					media.Parse(false);

					//取得時間長度
					long? duration = null;
					if (media is IMediaFromFile)
						duration = (media as IMediaFromFile).Duration;

					//取畫面大小
					Size<int> videoPixelSize;
					if (media is IMediaFromFile)
						videoPixelSize = this.GetVideoPixelSize(media);
					else
						videoPixelSize = new Size<int>();

					//儲存位元率
					//this.SetDecodedAverageBitRate(version, bitRate);
					//this.SetDecodedMaxBitRate(version, bitRate);

					//取得縮圖及影片資訊
					if (decodeThumb
						|| (decodeDuration && duration == null)
						|| (decodeVideoPixelSize && videoPixelSize == null))
					{
						//建立播放元件
						SemaphoreSlim semaphore = new SemaphoreSlim(0);
						videoPlayer = _MediaPlayerFactory.CreatePlayer<IVideoPlayer>();
						videoPlayer.Open(media);
						videoPlayer.Events.PlayerLengthChanged += (sender, e) =>
						{
							if (duration == null)
								duration = e.NewLength;
							if (videoPixelSize.IsEmpty)
								videoPixelSize = this.GetVideoPixelSize(media);
							if (!decodeThumb)
								semaphore.Release();
						};

						//擷取畫面及影片資訊
						byte[] frameBuffer = null;
						IMemoryRendererEx videoRenderer = videoPlayer.CustomRendererEx;
						try
						{
							if (decodeThumb)
							{
								float framePosition = (lockedAsFile ? Math.Min(0.5f, (10000f / duration.Value)) : 0);
								videoRenderer.SetFormatSetupCallback((format) =>
								{
									return new BitmapFormat(Math.Max(1, videoPixelSize.Width), Math.Max(1, videoPixelSize.Height), ChromaType.RGBA);
								});
								videoPlayer.Events.PlayerPlaying += (sender, e) =>
								{
									if (framePosition > 0)
										videoPlayer.Position = framePosition;
								};
								videoPlayer.Events.PlayerPositionChanged += (sender, e) =>
								{
									if (e.NewPosition >= framePosition && frameBuffer == null)
									{
										PlanarFrame frame = videoRenderer.CurrentFrame;
										int dataSize = frame.Lenghts[0];
										byte[] buffer = new byte[dataSize];
										Marshal.Copy(frame.Planes[0], buffer, 0, dataSize);
										frameBuffer = buffer;
										semaphore.Release();
									}
								};
							}
							videoPlayer.Volume = 0;
							videoPlayer.Mute = true;
							videoPlayer.Play();
							for (int i = (lockedAsFile ? 4 : 10); i > 0; --i)
							{
								if (semaphore.Wait(500) || this.IsCanceling)
									break;
							}
						}
						finally
						{
							videoPlayer.Stop();
							videoRenderer.Dispose();
						}
						if (this.IsCanceling)
							return true;
						if (decodeThumb && !videoPixelSize.IsEmpty)
						{
							IBitmapImage thumb;
							if (frameBuffer != null)
							{
								for (int i = 0, count = (videoPixelSize.Width * videoPixelSize.Height); count > 0; i += 4, --count)
								{
									byte r = frameBuffer[i];
									frameBuffer[i] = frameBuffer[i + 2];
									frameBuffer[i + 2] = r;
								}
								thumb = new iNet.Media.Imaging.BitmapImage(null, System.Windows.Media.Imaging.BitmapSource.Create(videoPixelSize.Width, videoPixelSize.Height, 96, 96, System.Windows.Media.PixelFormats.Bgra32, null, frameBuffer, (videoPixelSize.Width * 4)), true, null, null);
							}
							else
							{
								frameBuffer = new byte[videoPixelSize.Width * videoPixelSize.Height * 3];
								thumb = new BitmapImage(videoPixelSize.Width, videoPixelSize.Height, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, frameBuffer, videoPixelSize.Width * 3);
							}
							this.SetDecodedThumbnailImage(version, thumb);
						}
					}

					//儲存影片資訊
					if (duration != null)
						this.SetDecodedDuration(version, duration.Value);
					if (!videoPixelSize.IsEmpty)
						this.SetDecodedVideoPixelSize(version, videoPixelSize);
				}
				catch (Exception ex)
				{
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.Unknown, ex);
				}
				finally
				{
					if (videoPlayer != null)
						videoPlayer.Dispose();
					if (media != null)
						media.Dispose();
					if (lockedAsFile)
						this.ReleaseMediaStorageFile(version, source, videoPath);
				}
			}

			//解碼其他影片資訊
			//

			//解碼中繼資料
			IMetadata metadata = null;
			//

			//解碼影片
			if ((videoDecodeReqs & Video.VideoDecodeRequests.DecodeVideo) != 0)
			{
				try
				{
					VlcVideo video = new VlcVideo(source, true, metadata, this.DecodingFormat);
					if (!this.SetDecodedVideo(version, video))
					{
						video.Dispose();
						return true;
					}
				}
				catch (Exception ex)
				{
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.Unknown, ex);
				}
			}

			//完成
			return true;
		}
		#endregion
		#region GetVideoPixelSize
		Size<int> GetVideoPixelSize(Declarations.Media.IMedia media)
		{
			try
			{
				MediaTrack[] tracks = media.TracksInfoEx;
				long bitRate = 0;
				if (tracks != null)
				{
					for (int i = tracks.Length - 1; i >= 0; --i)
					{
						VideoTrack videoTrack = (tracks[i] as VideoTrack);
						if (videoTrack != null)
							return new Size<int>((int)videoTrack.Width, (int)videoTrack.Height);
					}
				}
				return new Size<int>();
			}
			catch
			{
				return new Size<int>();
			}
		}
		#endregion
		#region Yvu420ToRgb
		unsafe void Yvu420ToRgb(byte* yBuffer, int yBufferSize, byte* vBuffer, int vBufferSize, byte* uBuffer, int uBufferSize, int frameWidth, int frameHeight, byte[] rgbBuffer)
		{
			//確認來源資料大小
			//

			//

		}
		#endregion
	}
}
